package com.ryenlii.datatrans.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.eyelake.common.core.exception.BizException;
import com.eyelake.common.core.response.PageResult;
import com.eyelake.common.core.util.KsBeanUtil;
import com.eyelake.iot.common.domain.constants.InfluxDbConstants;
import com.eyelake.iot.common.domain.constants.IotCommonConstants;
import com.eyelake.iot.common.domain.dto.event.DeviceHistoryListDto;
import com.eyelake.iot.common.domain.dto.iot.DeviceHistoryExcelDto;
import com.eyelake.iot.common.domain.dto.tsdb.IotStorageDataDto;
import com.eyelake.iot.common.tsdb.base.dto.*;
import com.eyelake.iot.common.tsdb.base.service.IInfluxBaseService;
import com.ryenlii.datatrans.core.config.TdengineDbConfiguration;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * @Author wws
 * @Date 2023/5/10 10:57
 */
@Slf4j
@Service("tdDb")
public class TdengineDbServiceImpl implements IInfluxBaseService {

    @Resource
    private TdengineDbConfiguration tdengineDbConfig;

    @Override
    public void saveToDb(Object jsonObj) {
        List<IotStorageDataDto> iotDataList = (List<IotStorageDataDto>) jsonObj;
        for (IotStorageDataDto dto : iotDataList) {
            tdengineDbConfig.insertUsingTemplate(InfluxDbConstants.TDENGINE_TEMTEMPLATE_TABLE,
                    InfluxDbConstants.DEVICE_DATA_PREFIX + dto.getTableName() + "_" + dto.getTagMap()
                            .get(InfluxDbConstants.DEVICE_NAME) + "_" + dto.getTagMap()
                            .get(InfluxDbConstants.PROPERTY_CODE),
                    dto.getFieldMap(),
                    dto.getTagMap(),
                    dto.getUploadTime());
        }
    }

    @Override
    public List selectBySql(String querySql) {
        List<Map<String, Object>> maps = tdengineDbConfig.executeQuery(querySql);
        return maps;
    }

    @Override
    public <E> List<E> select(String querySql, String measurementName, Class domainClass) {
        List list = tdengineDbConfig.getList(querySql, domainClass);
        return list;
    }

    @Override
    public Long selectCountFromInfluxBySql(String sql) {
        return tdengineDbConfig.executeCountQuery(sql);
    }

    @Override
    public PageResult<DeviceHistoryListDto> queryHistoryData(DeviceHistoryExcelDto deviceHistoryExcelDto,
                                                             Map<String, String> map) {
        // 获取分页数据
        int currentPage = deviceHistoryExcelDto.getCurrentPage() == null ? 1 : deviceHistoryExcelDto.getCurrentPage();
        int pageSize = deviceHistoryExcelDto.getPageSize() == null ? 10 : deviceHistoryExcelDto.getPageSize();
        int offset = (currentPage - 1) * pageSize;
        HistoryDataTableDto historyDataTableDto = KsBeanUtil.copyProperties(deviceHistoryExcelDto,
                HistoryDataTableDto.class);
        StringBuffer countSql = queryHistoryDataTableSql(historyDataTableDto, Boolean.TRUE);
        // 查询数量
        Long count = selectCountFromInfluxBySql(countSql.toString());
        // 定义查询sql
        StringBuffer querySql = queryHistoryDataTableSql(historyDataTableDto, Boolean.FALSE);
        querySql.append(StrUtil.format(IotCommonConstants.INFLUXDB_PAGE, pageSize, offset));
        // 查询结果
        List<DevicePropertiesTransformDto> devicePropertyTransformDtos = select(querySql.toString(),
                InfluxDbConstants.DEVICE_DATA_PREFIX + deviceHistoryExcelDto.getProductKey(),
                DevicePropertiesTransformDto.class);

        List<DeviceProperties> list = devicePropertyTransformDtos.stream().map(dto -> {
            DeviceProperties vo = new DeviceProperties();
            vo.setDeviceName(dto.getDevice_name());
            vo.setTime(dto.getTime());
            vo.setPropertyCode(dto.getProperty_code());
            vo.setValue(dto.getValue_str());
            return vo;
        }).collect(Collectors.toList());
        // 对结果进行处理（最终返回）
        List<DeviceHistoryListDto> historyListDtoList = new ArrayList<>();
        if (CollUtil.isNotEmpty(list)) {
            for (DeviceProperties deviceProperties : list) {
                DeviceHistoryListDto deviceHistoryListDto = new DeviceHistoryListDto();

                deviceHistoryListDto.setPropertyCode(deviceProperties.getPropertyCode());
                deviceHistoryListDto.setDeviceName(deviceProperties.getDeviceName());
                // deviceHistoryListDto.setValue(deviceProperties.getValue());
                deviceHistoryListDto.setPropertyName(map.get(deviceProperties.getPropertyCode()));
                // 处理数据保留两位小数
                if (ObjectUtil.isNotNull(deviceProperties.getValue()) && Validator.isNumber(
                        deviceProperties.getValue())) {
                    try {
                        // string类型转double
                        double v = Double.parseDouble(deviceProperties.getValue());
                        deviceHistoryListDto.setValue(String.format("%.2f", v));
                    } catch (Exception e) {
                        log.error("强转double失败");
                        continue;
                    }
                } else {
                    deviceHistoryListDto.setValue(deviceProperties.getValue());
                }

                deviceHistoryListDto.setTime(LocalDateTimeUtil.of(deviceProperties.getTime()));

                historyListDtoList.add(deviceHistoryListDto);
            }

        }

        return PageResult.ok(historyListDtoList, count);
    }

    @Override
    public StringBuffer queryHistoryDataChartSql(HistoryDataChartDto historyDataChartDto) {
        historyDataChartDto.setAggregateMode(getFunction(historyDataChartDto.getAggregateMode()));
        if (StrUtil.isEmpty(historyDataChartDto.getTimeGroup()) && StrUtil.isNotEmpty(
                historyDataChartDto.getAggregateMode())) {
            throw BizException.getInstance("时间组不能为空");
        }
        if (StrUtil.isNotEmpty(historyDataChartDto.getTimeGroup()) && StrUtil.isEmpty(
                historyDataChartDto.getAggregateMode())) {
            throw BizException.getInstance("聚合模式不能为空");
        }
        if (StrUtil.isEmpty(historyDataChartDto.getTslCode())) {
            throw BizException.getInstance("设备属性不能为空");
        }
        StringBuffer querySql = new StringBuffer();
        Long startTime = LocalDateTimeUtil.toEpochMilli(historyDataChartDto.getStartTime());
        Long endTime = LocalDateTimeUtil.toEpochMilli(historyDataChartDto.getEndTime());
        // 有频率就取平均值，没有查所有
        if (Objects.nonNull(historyDataChartDto.getAggregateMode())) {
            querySql.append("select ")
                    .append("last(time) as time, ")
                    .append(historyDataChartDto.getAggregateMode())
                    .append("(")
                    .append(InfluxDbConstants.PROPERTY_VALUE_NUM)
                    .append(") as value_str from ");
        } else {
            querySql.append("select ")
                    .append(InfluxDbConstants.PROPERTY_VALUE_NUM)
                    .append("as value_str, time, property_code , device_name from ");
        }
        if (StrUtil.isNotBlank(historyDataChartDto.getTslCode()) && !IotCommonConstants.ALL.equals(
                historyDataChartDto.getTslCode())) {
            querySql.append(InfluxDbConstants.DEVICE_DATA_PREFIX)
                    .append(historyDataChartDto.getProductKey() + "_" + historyDataChartDto.getDeviceName() + "_"
                            + historyDataChartDto.getTslCode());
        } else {
            querySql.append(InfluxDbConstants.TDENGINE_TEMTEMPLATE_TABLE);
        }
        querySql.append(" where ")
                .append(InfluxDbConstants.DEVICE_NAME)
                .append("= '")
                .append(historyDataChartDto.getDeviceName())
                .append("'")
                .append(" and ")
                .append(InfluxDbConstants.PRODUCT_KEY)
                .append("= '")
                .append(historyDataChartDto.getProductKey())
                .append("'");
        // 判断是否有参数
        if (StrUtil.isNotBlank(historyDataChartDto.getTslCode()) && !IotCommonConstants.ALL.equals(
                historyDataChartDto.getTslCode())) {
            querySql.append(" and ")
                    .append(InfluxDbConstants.PROPERTY_CODE)
                    .append(" = '")
                    .append(historyDataChartDto.getTslCode())
                    .append("'");
        }
        querySql.append(" and time >=")
                .append(timeToString(startTime))
                .append(" and time <=")
                .append(timeToString(endTime));
        // 判断是否有频率值
        if (StrUtil.isNotEmpty(historyDataChartDto.getTimeGroup())) {
            querySql.append(" interval(")
                    .append(historyDataChartDto.getTimeGroup())
                    .append(") fill(NONE)");
        }
        querySql.append(" order by time desc limit 3000");
        log.info("查询图表SQl:{}", querySql.toString());
        return querySql;
    }

    @Override
    public StringBuffer queryHistoryDataTableSql(HistoryDataTableDto historyDataTableDto, Boolean countFlag) {
        StringBuffer querySql = new StringBuffer();
        Long startTime;
        Long endTime;
        // 不传时间默认7天
        if (Objects.isNull(historyDataTableDto.getStartTime()) && Objects.isNull(historyDataTableDto.getEndTime())) {
            startTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now().plusDays(7));
            endTime = LocalDateTimeUtil.toEpochMilli(LocalDateTime.now());
        } else {
            startTime = LocalDateTimeUtil.toEpochMilli(historyDataTableDto.getStartTime());
            endTime = LocalDateTimeUtil.toEpochMilli(historyDataTableDto.getEndTime());
        }
        if (countFlag) {
            querySql.append("select count(")
                    .append(InfluxDbConstants.PROPERTY_VALUE_STR)
                    .append(") from ");
        } else {
            querySql.append("select ")
                    .append("time ,")
                    .append(InfluxDbConstants.PROPERTY_VALUE_STR)
                    .append(", device_name , property_code from ");
        }
        if (StrUtil.isNotBlank(historyDataTableDto.getPropertyCode()) && !IotCommonConstants.ALL.equals(
                historyDataTableDto.getPropertyCode())) {
            querySql.append(InfluxDbConstants.DEVICE_DATA_PREFIX)
                    .append(historyDataTableDto.getProductKey() + "_" + historyDataTableDto.getDeviceName() + "_"
                            + historyDataTableDto.getPropertyCode());
        } else {
            querySql.append(InfluxDbConstants.TDENGINE_TEMTEMPLATE_TABLE);
        }
        querySql.append(" where ")
                .append(InfluxDbConstants.DEVICE_NAME)
                .append("= '")
                .append(historyDataTableDto.getDeviceName())
                .append("'")
                .append(" and ")
                .append(InfluxDbConstants.PRODUCT_KEY)
                .append("= '")
                .append(historyDataTableDto.getProductKey())
                .append("'")
        ;

        // 判断是否有参数
        if (StrUtil.isNotBlank(historyDataTableDto.getPropertyCode()) && !IotCommonConstants.ALL.equals(
                historyDataTableDto.getPropertyCode())) {
            querySql.append(" and ")
                    .append(InfluxDbConstants.PROPERTY_CODE)
                    .append(" = '")
                    .append(historyDataTableDto.getPropertyCode())
                    .append("'");
        }
        if (StrUtil.isNotBlank(historyDataTableDto.getTslCode()) && !IotCommonConstants.ALL.equals(
                historyDataTableDto.getPropertyCode())) {
            querySql.append(" and ")
                    .append(InfluxDbConstants.PROPERTY_CODE)
                    .append(" = '")
                    .append(historyDataTableDto.getTslCode())
                    .append("'");
        }
        querySql.append(" and time >=")
                .append(timeToString(startTime))
                .append(" and time <=")
                .append(timeToString(endTime));
        if (!countFlag) {
            querySql.append(" order by time desc");
        }
        return querySql;
    }

    @Override
    public StringBuffer queryHistoryDataStatSql(HistoryDataStatDto historyDataStatDto) {
        // 拼sql
        StringBuffer querySql = new StringBuffer();
        Long startTime =
                LocalDateTimeUtil.toEpochMilli(historyDataStatDto.getStartTime());
        Long endTime =
                LocalDateTimeUtil.toEpochMilli(historyDataStatDto.getEndTime());
        querySql.append("select last(time) as time, last(property_code) as property_code , ");
        for (String statType : historyDataStatDto.getStatType()) {
            if (statType.equals("mean")) {
                querySql.append("avg").append("(value_num) as ")
                        .append(statType)
                        .append(" ,");
            } else {
                querySql.append(statType).append("(value_num) as ")
                        .append(statType)
                        .append(" ,");
            }
        }
        // 删除最后一个逗号
        querySql.deleteCharAt(querySql.length() - 1);
        querySql.append(" from ");
        if (StrUtil.isNotBlank(historyDataStatDto.getPropertyCode()) && !IotCommonConstants.ALL.equals(
                historyDataStatDto.getPropertyCode())) {
            querySql.append(InfluxDbConstants.DEVICE_DATA_PREFIX)
                    .append(historyDataStatDto.getProductKey() + "_" + historyDataStatDto.getDeviceName() + "_"
                            + historyDataStatDto.getPropertyCode());
        } else {
            querySql.append(InfluxDbConstants.TDENGINE_TEMTEMPLATE_TABLE);
        }
        querySql.append(" where ").append(InfluxDbConstants.DEVICE_NAME)
                .append("= '")
                .append(historyDataStatDto.getDeviceName())
                .append("'")
                .append(" and ")
                .append(InfluxDbConstants.PRODUCT_KEY)
                .append("= '")
                .append(historyDataStatDto.getProductKey())
                .append("'");
        if (StrUtil.isNotBlank(historyDataStatDto.getPropertyCode()) && !IotCommonConstants.ALL.equals(
                historyDataStatDto.getPropertyCode())) {
            querySql.append(" and ")
                    .append(InfluxDbConstants.PROPERTY_CODE)
                    .append(" = '")
                    .append(historyDataStatDto.getPropertyCode())
                    .append("'");
        }
        querySql.append(" and time >=")
                .append(timeToString(startTime))
                .append(" and time <=")
                .append(timeToString(endTime));
        // 判断是否有频率值
        querySql.append(" interval(").append(historyDataStatDto.getCycle());
        // 没有单位默认按小时
        if (StrUtil.isNotBlank(historyDataStatDto.getCycleUnit())) {
            querySql.append(historyDataStatDto.getCycleUnit());
        } else {
            querySql.append("h");
        }
        querySql.append(") fill(none)");
        return querySql;
    }

    private String getFunction(String influxDbFunction) {
        if ("mean".equals(influxDbFunction)) {
            return "avg";
        }
        // 其他的聚合函数 iotdb和influxdb一样
        return influxDbFunction;
    }

    private String timeToString(Long time) {
        StringBuffer str = new StringBuffer();
        DateTimeFormatter ftf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.MS");
        str.append("'");
        str.append(ftf.format(LocalDateTime.ofInstant(Instant.ofEpochMilli(time), ZoneId.systemDefault())));
        str.append("'");
        return str.toString();
    }
}
